using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using Palaso.Reporting;
using SpellCenter.Core;

namespace SpellCenter.UI
{
    public class WordContextControlPM : IDisposable
    {
        private IList<WordContext> _data;
        private WordContextBackgroundWorker _backgroundWorker;
        private bool _isDisposed=false;

        public WordContextControlPM()
        {
            _data = new List<WordContext>();
        }

        public IList<WordContext> Data
        {
            get { return _data; }
        }

        public void SetSpellWords(IList<SpellWordInfo> words)
        {
            if(_backgroundWorker != null)
            {
                _backgroundWorker.Dispose();
            }
            _backgroundWorker = new WordContextBackgroundWorker(words);
            _backgroundWorker.Waiting += BackgroundWorker_Waiting;
            _backgroundWorker.DoneWaiting += BackgroundWorker_DoneWaiting;

        }

        #region Waiting and DoneWaiting events

        public event EventHandler Waiting;

        public event EventHandler DoneWaiting;

        private void BackgroundWorker_DoneWaiting(object sender, EventArgs e)
        {
            EventHandler handler = DoneWaiting;
            if (handler != null)
            {
                handler(sender, e);
            }

        }

        private void BackgroundWorker_Waiting(object sender, EventArgs e)
        {
            EventHandler handler = Waiting;
            if (handler != null)
            {
                handler(sender, e);
            }
        }

        #endregion


        #region FillContentByPixelWidth

        public void FillContextByPixelWidth(int word, int width, Font font)
        {
            Logger.WriteMinorEvent("Requesting word# {0} from background worker.", word);
            _backgroundWorker.Request(word,width, font);
            while(true)
            {
                _backgroundWorker.WaitForFinish();
                try
                {
                    _data = _backgroundWorker.GetRequestedResults();
                    break;
                }
                catch(InvalidOperationException e)
                {
                    Logger.WriteEvent("Caught InvalidOperationException, attemtint to recover:\n{0}", e.StackTrace);
                }
            }
        }

        public static List<WordContext> GetContentByPixelWidth(SpellWordInfo word, int width, Font font)
        {
            List<WordContext> ret = new List<WordContext>();
            // this way we reach to the disk for only the maximum amount of data we'll ever need, no more
            float avgCharWidth = GetAvgCharWidth(word, width, font);
            int charsToGet = (int) (width/avgCharWidth)/2;

            foreach (WordContext context in word.GetOccurrencesOfWord(charsToGet))
            {
                ret.Add(context);
            }
            return ret;
        }

        private static float GetAvgCharWidth(SpellWordInfo word, int width, Font font)
        {
            float avgCharWidth = 1;
            IEnumerator<WordContext> enumerator = word.GetOccurrencesOfWord(width).GetEnumerator();
            if(enumerator.MoveNext())
            {
                WordContext current = enumerator.Current;
                int textWidth = TextRenderer.MeasureText(current.Context, font).Width;
                avgCharWidth = textWidth/(float) current.Context.Length;
                enumerator.Dispose();
            }
            return avgCharWidth;
        }

        #endregion

        public string[] GetDisplayableStrings(int rowIndex)
        {
            string[] ret = new string[3] { "", "", "" };

            if (rowIndex >= 0 && rowIndex < Data.Count)
            {
                WordContext data = Data[rowIndex];
                Debug.Assert(data.Offset >= 0, string.Format("data.Offset={0}", data.Offset));
                ret[0] = data.Context.Substring(0, data.Offset);
                ret[1] = data.Word;
                ret[2] = data.Context.Substring(data.Offset + data.Word.Length);
            }
            return ret;
        }


              #region Disposal

        private void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                if (disposing)
                {
                    // dispose-only, i.e. non-finalizable logic
                    if (_backgroundWorker != null)
                    {
                        _backgroundWorker.DoneWaiting -= BackgroundWorker_DoneWaiting;
                        _backgroundWorker.Waiting -= BackgroundWorker_Waiting;
                        _backgroundWorker.Dispose();
                        _backgroundWorker = null;
                    }
                }

                // shared (dispose and finalizable) cleanup logic
                this._isDisposed = true;
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected void VerifyNotDisposed()
        {
            if (this._isDisposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
        }

        ~WordContextControlPM()
        {
            if (!this._isDisposed)
            {
                throw new InvalidOperationException("Disposed not explicitly called on " + GetType().FullName + ".");
            }
        }

        #endregion
    }
}